package com.boarsoft.message.bean;

import java.io.Serializable;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

import com.boarsoft.common.Util;
import com.boarsoft.common.util.JsonUtil;
import com.boarsoft.message.MessageConfig;

/**
 * 消息与方法调用的区别在于消息有优先级，有传播属性（点对点还是发布订阅），可以持久化<br>
 * catalog/target/key的组合决定消息的保存位置
 * 
 * @author Mac_J
 *
 */
public class Message implements Serializable, Comparable<Message> {
	private static final long serialVersionUID = 7957833198236168058L;

	/** 新生成或已持久化，但未入队的消息 */
	public static final short STATUS_NEW = 0;
	/** 已入队准备发送的消息 */
	public static final short STATUS_QUEUED = 1;
	/** 已拆分到收件人队列 */
	public static final short STATUS_SENDING = 2;
	/** 已成功发送给（必要的）收件人，并不代表消息被成功消费 */
	public static final short STATUS_SENT = 3;
	/** 已成功发送给（必要的）收件人，并不代表消息被成功消费 */
	public static final short STATUS_DONE = 4;
	/** 至少一个（必要的）收件人未能发送成功（但存在一定机率，对方已收到了消息） */
	public static final short STATUS_FAILED = 5;
	/** 消息配置存在问题，导致消息未能送出 */
	public static final short STATUS_INVALID = 6;

	/** 发送消息签收失败，此状态仅在消息服务器和消息接收方有效 */
	public static final short STATUS_ACK_FAILED = 7;
	/** 发送消息签收成功，此状态仅在消息服务器和消息接收方有效 */
	public static final short STATUS_ACK_SENDING = 8;
	/** 发送消息签收成功，此状态仅在消息服务器和消息接收方有效 */
	public static final short STATUS_ACK_SENT = 9;
	/** 消息（收件人）已成功消费 */
	public static final short STATUS_CONSUMED = 10;

	/** 持久化时自动生成 */
	protected String id;
	/** queueId or topicId */
	protected String target;
	/** 用于接收签收消息的队列名 */
	protected String acknowledge;
	/** 发件人 */
	protected String from;
	/** 发件人地址 */
	protected String fromAddr = MessageConfig.getAddr();
	/** 是否使用兄弟节点发送 */
	protected boolean relay;
	/** */
	protected int tried;
	/** 收件人，及收发状态，即 MessageService */
	protected Map<String, RecipientStatus> recipients = //
			new ConcurrentHashMap<String, RecipientStatus>();
	/** 本消息相对于catalog/target的唯一标识 */
	protected String key;
	/** 自定义标签 */
	protected String tag;
	/** 消息的title */
	protected String title;
	/** 消息内容 */
	protected Object content;
	/** 消息附件 */
	protected Object attachment;
	/** 消息附加属性 */
	protected Properties properties;
	/** 当前状态 */
	protected short status = STATUS_NEW;
	/** 消息生成时间 */
	protected long time = new Date().getTime();

	protected List<String> traces = new LinkedList<String>();

	@Override
	public int compareTo(Message m) {
		if (m == null) {
			return -1;
		}
		if (m.time > time) {
			return 1;
		} else if (m.time == time) {
			return 0;
		}
		return -1;
	}

	public short status() {
		if (status != STATUS_SENDING) {
			return status;
		}
		int success = 0, failed = 0;
		for (RecipientStatus s : recipients.values()) {
			switch (s.getStatus()) {
			case RecipientStatus.SUCCESS:
				success++;
				break;
			case RecipientStatus.FAILED:
				failed++;
				if (s.isRequired()) {
					// 此时不能return，因为可能还有未发过的收件人
					status = STATUS_FAILED;
				}
				break;
			case RecipientStatus.PENDING:
				status = STATUS_SENDING;
				// 只要有一个是未发送过，都是未完成，需要向所有收件人发送
				return status;
			}
		}
		if (status == STATUS_FAILED) {
			return status;
		}
		if (success == 0) {
			status = STATUS_FAILED;
			return status;
		}
		if (failed > 0) {
			status = STATUS_DONE; // 有成功也有失败就是DONE
		} else {
			status = STATUS_SENT; // 全部成功就是SENT成功
		}
		return status;
	}

	public void setRecipientStatus(String recipient, short status, int tried, String memo) {
		RecipientStatus rs = this.getRecipientStatus(recipient);
		rs.setStatus(status);
		rs.setTried(tried);
		rs.setMemo(memo);
	}

	public int tried1() {
		tried++;
		return tried;
	}

	public int tried1(String recipient) {
		return this.getRecipientStatus(recipient).tried1();
	}

	public String toString() {
		return id;
	}

	public String toJson() {
		return JsonUtil.toJSONString(this);
	}

	public short getStatus() {
		return status;
	}

	public void setStatus(short status) {
		this.status = status;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getKey() {
		return key;
	}

	public void setKey(String key) {
		this.key = key;
	}

	public Map<String, RecipientStatus> getRecipients() {
		return recipients;
	}

	public RecipientStatus getRecipientStatus(String recipient) {
		return recipients.get(recipient);
	}

	public void addRecipient(String recipient) {
		this.addRecipient(recipient, true);
	}

	public void addRecipient(String recipient, boolean required) {
		if (Util.strIsEmpty(recipient)) {
			throw new IllegalArgumentException("Can not add empty recipient");
		}
		recipients.put(recipient, new RecipientStatus(required));
	}

	public void setRecipients(Map<String, RecipientStatus> recipients) {
		if (recipients == null) {
			return;
		}
		this.recipients = recipients;
	}

	public void addRecipients(List<String> recipients) {
		this.addRecipients(recipients, true);
	}

	public void addRecipients(List<String> recipients, boolean required) {
		for (String r : recipients) {
			this.recipients.put(r, new RecipientStatus(required));
		}
	}

	public void addRecipients(Map<String, Boolean> recipients) {
		for (Entry<String, Boolean> en : recipients.entrySet()) {
			this.recipients.put(en.getKey(), new RecipientStatus(en.getValue()));
		}
	}

	public void addTrace(String addr) {
		traces.add(addr);
	}

	public String lastTrace() {
		if (traces.isEmpty()) {
			return null;
		}
		return traces.get(traces.size() - 1);
	}

	public String getTarget() {
		return target;
	}

	public void setTarget(String target) {
		this.target = target;
	}

	public Object getContent() {
		return content;
	}

	public void setContent(Object content) {
		this.content = content;
	}

	public boolean isNeedAcknowledge() {
		return Util.strIsNotEmpty(acknowledge);
	}

	public String getAcknowledge() {
		return acknowledge;
	}

	public void setAcknowledge(String acknowledge) {
		this.acknowledge = acknowledge;
	}

	public String getFrom() {
		return from;
	}

	public void setFrom(String from) {
		this.from = from;
	}

	public Object getAttachment() {
		return attachment;
	}

	public void setAttachment(Object attachment) {
		this.attachment = attachment;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public long getTime() {
		return time;
	}

	public void setTime(long time) {
		this.time = time;
	}

	public void setTime(Long time) {
		if (time == null) {
			return;
		}
		this.time = time;
	}

	public String getFromAddr() {
		return fromAddr;
	}

	public void setFromAddr(String fromAddr) {
		this.fromAddr = fromAddr;
	}

	public int getTried() {
		return tried;
	}

	public void setTried(int tried) {
		this.tried = tried;
	}

	public int getTried(String recipient) {
		return this.getRecipientStatus(recipient).getTried();
	}

	public boolean isRelay() {
		return relay;
	}

	public void setRelay(boolean relay) {
		this.relay = relay;
	}

	public List<String> getTraces() {
		return traces;
	}

	public void setTraces(List<String> traces) {
		this.traces = traces;
	}

	public String getTag() {
		return tag;
	}

	public void setTag(String tag) {
		this.tag = tag;
	}

	public Properties getProperties() {
		return properties;
	}

	public void setProperties(Properties properties) {
		this.properties = properties;
	}

}
